package game
import (
	"utils/log"
	"message"
	"game/poker/hand"
	"github.com/golang/protobuf/proto"
)

//--------------------结果和奖励判定------------------------

type Winner struct {
	player *User
	score  hand.Ranking
	cards  []int32
}

// 摊牌比大小 返回的第一个数组是亮牌的合集  第二个是封牌（可不亮牌）的合集
func (self *Room) updateShowdown() {
	log.Debug("updateShowdown")
	/*
	# 实际比赛的亮牌顺序是从最后一轮最后一个跟注或者加注的玩家（如果没有则从小盲注开始）开始顺时针亮牌，如果认定输了可以不亮牌，凡是赢得奖池的人必须亮牌
	# 在游戏中，为了节约时间，简化处理为从上述第一人开始，到赢得奖池的人，之间的所有人必须亮牌。其余人可以选择亮牌和不亮牌
	# 全下的人必须亮牌
	# 弃牌的人可以选择亮牌或者不亮牌
	# 如果全部人都弃牌，赢家可以选择不亮牌
	*/
	players := self.playersNotFold()
	if len(players) == 1 {
		// 其他人都弃牌了，则胜者可以不亮牌
		for _, player := range self.playersInSeat {
			if player != nil && player.state != message.PlayerState_STATE_STAND && player.autoShowCard {
				self.broadcastShowCard(player)
			}
		}
		return
	}

	var showList []*User
	var muckList []*User

	/*
	 pred = lambda x : x.status == STATUS_PLAYING or x.status == STATUS_ALL_IN;

        # firstPlayer = None
        # if self.lastBetPlayerInRiver:
        #     firstPlayer = self.lastBetPlayerInRiver
        # else:
        #     firstPlayer = self.getNextPlayer(self.dealerSeat, pred)

        # # 最后一个亮牌的玩家
        # lastPlayer = self.getPrevPlayer(firstPlayer.seat, pred)

        # player = firstPlayer
        # while True:
        #     show = False

        #     # all-in的玩家必须亮牌
        #     if player.status == STATUS_ALL_IN:
        #         show = True

        #     # 赢得奖池的人必须亮牌
        #     # if player in self.winners:
        #     #     show = True

        #     # TODO 先简化处理，所有没弃牌的玩家都亮牌
        #     showList.append(player)
        #     # if show:
        #     #     showList.append(player)
        #     # else:
        #     #     muckList.append(player)

        #     if player.playerID == lastPlayer.playerID:
        #         break

        #     player = self.getNextPlayer(player.seat, pred)
	 */

	// TODO 先简化处理，所有没弃牌的玩家都亮牌
	for _, player := range self.playersNotFold() {
		showList = append(showList, player)
	}

	// 亮牌
	for _, player := range showList {
		self.broadcastShowCard(player)
	}

	// 如果封牌队列里的玩家有结束时自动亮牌的，则也亮牌
	for _, player := range muckList {
		if player.autoShowCard {
			self.broadcastShowCard(player)
		}
	}
}

// 把玩家的押注归到奖池里面，根据all-in情况可能产生n个边池
func (self *Room) addBetsToPot() {
	log.Debug("addBetsToPot")
	smallestBet := 0
	allBetsAreEqual := true
	if len(self.pots) <= 0 {
		return
	}

	pot := self.pots[len(self.pots) - 1]

	// 遍历所有的押注
	for _, bet := range self.bets {
		if smallestBet == 0 {
			smallestBet = bet
			continue
		}

		// 并不是所有的押注都相等
		if bet != smallestBet {
			allBetsAreEqual = false
		}

		// 获取最小的一个押注
		if bet < smallestBet {
			smallestBet = bet
		}
	}

	if allBetsAreEqual {
		// 如果所有的押注都是相等的 则直接把钱归到当前池中
		for playerID, bet := range self.bets {
			if bet > 0 {
				pot.value += bet
				self.bets[playerID] = 0
				pot.contributors = append(pot.contributors, playerID)
			}
		}
	} else {
		/*
					# 如果有押注不等的情况，那么一定是某个人all-in了，按最小值加入当前池，并且产生新的边池
					# 产生边池，玩家的all-in行为可能产生边池
					# 限制一个玩家的最多赢取金额，一个只有1000块的玩家不可能把一个10000块玩家赢光，最多赢取额外的1000块
					# 主池是所有玩家可赢取的，边池是超出all-in玩家可赢取的金额的筹码，也就是说是除了all-in玩家之外的其他玩家可赢取的
		 */
		for playerID, bet := range self.bets {
			if bet > 0 {
				pot.value += smallestBet
				self.bets[playerID] = bet - smallestBet
				pot.contributors = append(pot.contributors, playerID)
			}
		}

		// 产生新的边池
		self.pots = append(self.pots, &Pot{value : 0})

		// 稍微加个判断，防止递归时死循环
		if len(self.pots) <= 10 {
			// 继续递归，直到所有的押注都处理完毕
			self.addBetsToPot()
		}
	}
}

// 根据玩家的手牌和场上的公牌计算最大牌分值
func (self *Room) evaluate(player *User) (*hand.Hand, hand.Ranking, []int32) {
	var cards []*hand.Card
	if player.handCard1 != nil {
		cards = append(cards, player.handCard1)
	}

	if player.handCard2 != nil {
		cards = append(cards, player.handCard2)
	}

	for _, v := range self.board {
		if v != nil {
			cards = append(cards, v)
		}
	}

	h := hand.New(cards)

	var retCards []int32
	for _, v := range h.Cards() {
		retCards = append(retCards, int32(v.Index()))
	}
	return h, h.Ranking(), retCards
}

// 根据牌值，返回牌型
func (self *Room) getCardTypeByScore(ranking hand.Ranking) message.CardType {
	switch ranking {
	case hand.HighCard:
		return message.CardType_HIGH_CARD
	case hand.Pair:
		return message.CardType_PAIR
	case hand.TwoPair:
		return message.CardType_TWO_PAIR
	case hand.ThreeOfAKind:
		return message.CardType_THREE_OF_A_KIND
	case hand.Straight:
		return message.CardType_STRAIGHT
	case hand.Flush:
		return message.CardType_FLUSH
	case hand.FullHouse:
		return message.CardType_FULL_HOUSE
	case hand.FourOfAKind:
		return message.CardType_FOUR_OF_A_KIND
	case hand.StraightFlush:
		return message.CardType_STRAIGHT_FLUSH
	case hand.RoyalFlush:
		return message.CardType_ROYAL_FLUSH
	}

	return message.CardType_HIGH_CARD
}

func (self *Room) distributePot() {
	log.Debug("distributePot")
	var result []*message.ResultInfo

	// 所有人都弃牌了，只有一个胜利者，不需要比牌，直接分配奖池
	players := self.playersNotFold()
	if len(players) == 1 {
		// 把奖池分配给玩家
		totalMoney := 0
		for _, pot := range self.pots {
			_ = pot
			totalMoney += pot.value
		}
		self.actionPlayerAwardMoney(players[0], totalMoney)
		_, score, cards := self.evaluate(players[0])
		result = append(result, &message.ResultInfo{
			Seat:proto.Int(players[0].seat),
			Money:proto.Int(totalMoney),
			IsSidePool:proto.Bool(false),
			CardType:self.getCardTypeByScore(score).Enum(),
			Cards:cards,
		})
		self.broadcastResult(result)
		return
	}

	var firstActPlayer *User
	for _, player := range self.playersInSeat {
		if player != nil {
			find := false
			for _, p := range players {
				if player == p {
					firstActPlayer = player
					find = true
					break
				}
			}
			if find {
				break
			}
		}
	}
	_ = firstActPlayer

	/*
	        # 需要比牌，有不止一个人all-in或者跟注到最后
        # 从最后一个奖池开始分，奖池越靠后，参与分配的人越少
        # TODO 关于uncalled bet,当因为all-in或者fold结束比赛的情况下，可能某个玩家的押注是比其他人都多的，这个时候他比第二高的押注还多出来的钱是无人可以赢取的
        #      这部分钱就是uncalled bet，可以理解为一个特殊的边池，它是直接返还给玩家的，这部分钱是不抽佣金的。
        #      另外，当最终因为跟注相等而结束比赛的情况下，也就是说最高押注存在多个并且相等的情况下，是不会存在uncalled bet的
        #      只有最后一个边池（即最少人分的奖池）才会有uncalled bet的情况，因为前面出现类似的情况的时候是产生新的边池
	 */

	for i := len(self.pots) - 1; i >= 0; i-- {
		pot := self.pots[i]
		amount := pot.value    // 当前奖池的总金额
		contributors := pot.contributors    // 当前奖池的贡献者

		// 只有一个玩家参与此边池，即uncalled bet的情况，这些钱直接返还给玩家，并且不抽取佣金
		if i == 0 && len(contributors) == 1 {
			winner := self.getPlayer(contributors[0])
			self.actionPlayerAwardMoney(winner, amount)
			_, score, cards := self.evaluate(winner)
			result = append(result, &message.ResultInfo{
				Seat:proto.Int(winner.seat),
				Money:proto.Int(amount),
				IsSidePool:proto.Bool(i != 0),
				CardType:self.getCardTypeByScore(score).Enum(),
				Cards:cards,
			})
			continue
		}

		var winners []*Winner
		var bestHand *hand.Hand = nil

		// 统计对应奖池的胜利者
		for _, playerID := range contributors {
			player := self.getPlayer(playerID)
			h, score, cards := self.evaluate(player)
			if bestHand == nil {
				bestHand = h
				winners = []*Winner{&Winner{player:player, score:score, cards:cards}}
				continue
			}

			comp := h.CompareTo(bestHand)
			if comp > 0 {
				bestHand = h
				// 如果有人分值高，那么他就是胜利者
				winners = []*Winner{&Winner{player:player, score:score, cards:cards}}
			} else if comp == 0 {
				// 如果出现最高分值相等的情况，那么平分对应奖池
				winners = append(winners, &Winner{player:player, score:score, cards:cards})
			}
		}

		winCount := len(winners)
		if winCount == 1 {
			// 只有一个胜利者的情况
			player := winners[0].player
			score := winners[0].score
			self.actionPlayerAwardMoney(player, amount)
			result = append(result, &message.ResultInfo{
				Seat:proto.Int(player.seat),
				Money:proto.Int(amount),
				IsSidePool:proto.Bool(i != 0),
				CardType:self.getCardTypeByScore(score).Enum(),
				Cards:winners[0].cards,
			})
		} else if winCount > 1 {
			// 每个人平分的钱
			avgChip := int(amount / winCount)
			// 无法平分剩下的钱
			leftChip := amount - avgChip * winCount

			// 玩家实际赢取的钱
			winChip := 0

			for _, data := range winners {
				player := data.player
				score := data.score
				if leftChip > 0 {
					//无法分配的剩下的钱归到第一个玩家  TODO 修改为庄家后的第一个赢的玩家
					winChip = avgChip + leftChip
					leftChip = 0
				} else {
					winChip = avgChip
				}

				// 玩家分钱
				self.actionPlayerAwardMoney(player, winChip)
				result = append(result, &message.ResultInfo{
					Seat:proto.Int(player.seat),
					Money:proto.Int(winChip),
					IsSidePool:proto.Bool(i != 0),
					CardType:self.getCardTypeByScore(score).Enum(),
					Cards:data.cards,
				})
			}
		}
	}

	self.broadcastResult(result)

}